﻿using System;
using System.IO;
using System.Text;

namespace Library.Util {
	public static class StreamUtil {
		private static readonly byte[] Emplty = new byte[0];
		private const int Int16Size = sizeof(Int16);
		private const int Int32Size = sizeof(Int32);
		private const int Int64Size = sizeof(Int64);

		private const int UInt16Size = sizeof(UInt16);
		private const int UInt32Size = sizeof(UInt32);
		private const int UInt64Size = sizeof(UInt64);

		private const int FloatSize = sizeof(Single);
		private const int DoubleSize = sizeof(Double);
		
		public static void Read(this Stream stream, out Byte value) {
			var b = stream.ReadByte();
			if (b == -1) {
				throw new EndOfStreamException();
			}
			value = (byte)b;
		}

		public static void Read(this Stream stream, out Int16 value) {
			var bs = ReadBytes(stream, Int16Size);
			value = BitConverter.ToInt16(bs, 0);
		}

		public static void Read(this Stream stream, out Int32 value) {
			var bs = ReadBytes(stream, Int32Size);
			value = BitConverter.ToInt32(bs, 0);
		}

		public static void Read(this Stream stream, out Int64 value) {
			var bs = ReadBytes(stream, Int64Size);
			value = BitConverter.ToInt64(bs, 0);
		}

		public static void Read(this Stream stream, out UInt16 value) {
			var bs = ReadBytes(stream, UInt16Size);
			value = BitConverter.ToUInt16(bs, 0);
		}

		public static void Read(this Stream stream, out UInt32 value) {
			var bs = ReadBytes(stream, UInt32Size);
			value = BitConverter.ToUInt32(bs, 0);
		}

		public static void Read(this Stream stream, out UInt64 value) {
			var bs = ReadBytes(stream, UInt64Size);
			value = BitConverter.ToUInt64(bs, 0);
		}
		public static void Read(this Stream stream, out Boolean value) {
			byte b;
			stream.Read(out b);
			value = b == 1;
		}
		public static void Read(this Stream stream, out Single value) {
			var bs = ReadBytes(stream, FloatSize);
			value = BitConverter.ToSingle(bs, 0);
		}
		public static void Read(this Stream stream, out Double value) {
			var bs = ReadBytes(stream, DoubleSize);
			value = BitConverter.ToDouble(bs, 0);
		}
		

		public static void Read(this Stream stream, out Decimal value) {
			int m1, m2, m3, m4;
			stream.Read(out m1);
			stream.Read(out m2);
			stream.Read(out m3);
			stream.Read(out m4);
			value = new decimal(new[] { m1, m2, m3, m4 });
		}

		public static void ReadAnsi(this Stream stream, out String value) {
			ushort len;
			Read(stream, out len);
			var bs = ReadBytes(stream, len);
			value = Encoding.ASCII.GetString(bs);
		}

		public static void ReadUnicode(this Stream stream, out String value) {
			ushort len;
			Read(stream, out len);
			var bs = ReadBytes(stream, len);
			value = Encoding.Unicode.GetString(bs);
		}

		public static void Read(this Stream stream, int len, out Byte[] value) {
			value = ReadBytes(stream, len);
		}

		private static byte[] ReadBytes(Stream stream, int len) {
			if (len == 0) return Emplty;
			int byteRead = 0;
			var bs = new byte[len];
			do {
				var read = stream.Read(bs, byteRead, len - byteRead);
				if (read == 0) {
					throw new EndOfStreamException();
				}
				byteRead += read;
			} while (byteRead < len);
			//for (int i = 0; i < bs.Length; i++) {
			//  var b = stream.ReadByte();
			//  if (b == -1) {
			//    throw new EndOfStreamException();
			//  }
			//  bs[i] = (byte)b;
			//})

			return bs;
		}


		public static void WriteUnicode(this Stream stream, String text) {
			if (text == null) text = String.Empty;
			var bs = Encoding.Unicode.GetBytes(text);
			var len = (ushort)bs.Length;
			Write(stream, len);
			Write(stream, bs);
		}
		public static void WriteAnsi(this Stream stream, String text) {
			if (text == null) text = String.Empty;
			var bs = Encoding.ASCII.GetBytes(text);
			var len = (ushort)bs.Length;
			Write(stream, len);
			Write(stream, bs);
		}

		public static void Write(this Stream stream, Int64 value) {
			var bs = BitConverter.GetBytes(value);
			Write(stream, bs);
		}
		public static void Write(this Stream stream, Single value) {
			var bs = BitConverter.GetBytes(value);
			Write(stream, bs);
		}
		public static void Write(this Stream stream, Double value) {
			var bs = BitConverter.GetBytes(value);
			Write(stream, bs);
		}
		public static void Write(this Stream stream, Int32 value) {
			var bs = BitConverter.GetBytes(value);
			Write(stream, bs);
		}
		public static void Write(this Stream stream, Int16 value) {
			var bs = BitConverter.GetBytes(value);
			Write(stream, bs);
		}

		public static void Write(this Stream stream, UInt64 value) {
			var bs = BitConverter.GetBytes(value);
			Write(stream, bs);
		}
		public static void Write(this Stream stream, Decimal value) {
			var bs = Decimal.GetBits(value);
			foreach (var i in bs) {
				Write(stream, i);
			}

		}
		public static void Write(this Stream stream, UInt32 value) {
			var bs = BitConverter.GetBytes(value);
			Write(stream, bs);
		}
		public static void Write(this Stream stream, UInt16 value) {
			var bs = BitConverter.GetBytes(value);
			Write(stream, bs);
		}
		public static void Write(this Stream stream, Boolean value) {
			byte b = value ? (byte)1 : (byte)0;
			stream.Write(b);
		}

		public static void Write(this Stream stream, Byte value) {
			stream.WriteByte(value);
		}

		public static void Write(this Stream stream, Byte[] bytes) {
			foreach (var b in bytes) {
				stream.WriteByte(b);
			}
		}
	}
}
